package com.juyu.miliao.api.dao.elasticsearch;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.juyu.miliao.api.dao.redis.FindDynamicLikeDaoRedis;
import com.juyu.miliao.api.dao.redis.usercache.UserCacheVo;
import com.juyu.miliao.api.dao.redis.usercache.UserInfoCache;
import com.juyu.miliao.api.domain.Appkey;
import com.juyu.miliao.api.domain.dto.FindDynamicElasticsearchDTO;
import com.juyu.miliao.api.domain.vo.FindDynamicDetailVO;
import com.juyu.miliao.api.domain.vo.FindDynamicVO;
import com.juyu.miliao.api.service.AppKeyService;
import com.juyu.miliao.api.service.FindDynamicInfoChannelService;
import com.juyu.miliao.api.util.PageUtil;
import com.juyu.miliao.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author xuanqicheng
 */
@Repository
@Slf4j
public class FindDynamicElasticsearch {

    public static final String AUDIT_USER_IDS_KEY = "openApiUserId";
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private AppKeyService appKeyService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private UserInfoCache userInfoCache;

    @Autowired
    private FindDynamicLikeDaoRedis findDynamicLikeDaoRedis;

    @Autowired
    private FindDynamicInfoChannelService findDynamicInfoChannelService;

    /**
     * @param userId
     * @param page
     * @param pageSize
     * @param isAudit  审核状态，true 为审核状态，false为关闭审核状态
     * @param channel  渠道
     * @return
     * @throws IOException
     */
    public List<FindDynamicVO> selectPage(long userId, int page, int pageSize, boolean isAudit, String channel) throws IOException {

        List<FindDynamicVO> resultList = new ArrayList<>();

        int startNum = PageUtil.getOffset(page, pageSize);

        SearchRequest searchRequest = getSearchRequest("find_dynamic_info");

        List<SortBuilder> sortBuilderList = new ArrayList<>(1);

        FieldSortBuilder isTopFieldSortBuilder = new FieldSortBuilder("isTop").order(SortOrder.DESC);

        sortBuilderList.add(isTopFieldSortBuilder);

        FieldSortBuilder fieldSortBuilder = new FieldSortBuilder("approvalCreateTime").order(SortOrder.DESC);

        sortBuilderList.add(fieldSortBuilder);

        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(pageSize, startNum, sortBuilderList);

        //false 不屏蔽 ,true 屏蔽
        List<TermsQueryBuilder> termsQueryBuilderList = getTermsQueryBuildersByShieldType(channel,isAudit);

        if (isAudit) {
            Appkey appkey = appKeyService.getAppKeyByKey(AUDIT_USER_IDS_KEY);
            if (appkey != null) {
                String target = appkey.getValue();
                if (StringUtils.isNotBlank(target)) {
                    String[] userIds = target.split(",");
                    log.info("审核使用的id=" + JSONObject.toJSONString(userIds));
                    termsQueryBuilderList.add(QueryBuilders.termsQuery("userId", userIds));

                } else {
                    return resultList;
                }
            } else {
                return resultList;
            }
        }

        if (termsQueryBuilderList.size() > 0) {
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            for (TermsQueryBuilder termsQueryBuilder : termsQueryBuilderList) {
                boolQuery.must(termsQueryBuilder);
            }
            searchSourceBuilder.query(boolQuery);
        } else {
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        }

        searchRequest.source(searchSourceBuilder);

        SearchResponse result = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<FindDynamicElasticsearchDTO> findDynamicElasticsearchList = getFindDynamicElasticsearchDTOS(result);


        for (FindDynamicElasticsearchDTO obj : findDynamicElasticsearchList) {
            FindDynamicVO findDynamicVO = getFindDynamicVO(userId, obj);
            findDynamicVO.setDistance("未知");
            resultList.add(findDynamicVO);
        }
//        log.info("isAudit="+isAudit+"|resultList="+JSONObject.toJSONString(resultList));
        return resultList;

    }



    /**
     * 根据findId查找数据
     *
     * @param findId
     * @return
     */
    public FindDynamicDetailVO getFindDynamicInfoByFindId(int findId) throws IOException {

        SearchRequest searchRequest = getSearchRequest("find_dynamic_info");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));

        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        boolQueryBuilder.must(QueryBuilders.termQuery("findId", findId));

        searchSourceBuilder.query(boolQueryBuilder);

        searchRequest.source(searchSourceBuilder);

        SearchResponse result = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<FindDynamicElasticsearchDTO> findDynamicElasticsearchList = getFindDynamicElasticsearchDTOS(result);
        if (findDynamicElasticsearchList.size() > 0) {
            FindDynamicElasticsearchDTO obj = findDynamicElasticsearchList.get(0);
            FindDynamicVO findDynamicVO = getFindDynamicVO(-1, obj);
            FindDynamicDetailVO findDynamicDetailVO = new FindDynamicDetailVO();
            BeanUtils.copyProperties(findDynamicVO, findDynamicDetailVO);
            return findDynamicDetailVO;
        } else {
            return null;
        }


    }

    /**
     * 设置返回信息
     *
     * @param userId                      请求用户id
     * @param findDynamicElasticsearchDTO
     * @return
     * @throws IOException
     */
    private FindDynamicVO getFindDynamicVO(long userId, FindDynamicElasticsearchDTO findDynamicElasticsearchDTO) throws IOException {

        FindDynamicVO findDynamicVO = new FindDynamicVO();

        BeanUtils.copyProperties(findDynamicElasticsearchDTO, findDynamicVO);

        getFindDynamicVOByUserInfo(findDynamicVO);


        boolean bl = findDynamicLikeDaoRedis.checkFindDynamicInfoByFindIdAndUserId(findDynamicVO.getFindId(),
                userId);
        if (bl) {
            findDynamicVO.setIsLike(1);
        } else {
            findDynamicVO.setIsLike(2);
        }

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//        log.info("设置FindDynamicVO：{}",findDynamicVO);
        if(StringUtils.isNotBlank(findDynamicVO.getApprovalCreateTime())){
            String showTime = DateUtil.getShowTime(LocalDateTime.parse(findDynamicVO.getApprovalCreateTime(), df));

            findDynamicVO.setShowTime(showTime);
        }else{
            findDynamicVO.setShowTime("未知");
        }


        return findDynamicVO;
    }

    /**
     * 设置用户信息
     *
     * @param findDynamicVO
     */
    public void getFindDynamicVOByUserInfo(FindDynamicVO findDynamicVO) {
        UserCacheVo userCacheVo = userInfoCache.getUserCacheInfo(findDynamicVO.getUserId());
        findDynamicVO.setNickName(userCacheVo.getNickName());
        findDynamicVO.setAge(userCacheVo.getAge());
        findDynamicVO.setSex(userCacheVo.getSex());
        findDynamicVO.setIsVip(userCacheVo.getIsVip());
        findDynamicVO.setIsHost(userCacheVo.getIsHost());
        findDynamicVO.setIcon(userCacheVo.getIcon());
        findDynamicVO.setStar(userCacheVo.getStar());
        findDynamicVO.setVipLevel(userCacheVo.getVipLevel());
        findDynamicVO.setMGrade(userCacheVo.getMGrade());
        findDynamicVO.setVGrade(userCacheVo.getVGrade());
        Integer level = userCacheVo.getHostUserLevel().getHostUserGrade();
        findDynamicVO.setHostUserGrade(level==null ? 0:level);
        String userGradePic = userCacheVo.getHostUserLevel()
                .getHostUserGradePic();
        findDynamicVO.setHostUserGradePic(userGradePic == null ? "":userGradePic);
        String levelPic = userCacheVo.getUserLevel().getLevelPic();
        findDynamicVO.setLevelPic(levelPic==null ? "":levelPic);
        findDynamicVO.setEnergyGrade(userCacheVo.getUserLevel().getEnergyGrade());
    }

    /**
     * Elasticsearch 转换
     *
     * @param result
     * @return
     * @throws IOException
     */
    private List<FindDynamicElasticsearchDTO> getFindDynamicElasticsearchDTOS(SearchResponse result) throws IOException {
        SearchHit[] searchHits = result.getHits().getHits();

        List<FindDynamicElasticsearchDTO> findDynamicElasticsearchList = new ArrayList<>();

        for (SearchHit hit : searchHits) {

            Object[] objects = hit.getSortValues();

            String target = objectMapper.writeValueAsString(hit.getSourceAsMap());

            FindDynamicElasticsearchDTO findDynamicElasticsearchDTO = objectMapper.readValue(target,
                    FindDynamicElasticsearchDTO.class);

            findDynamicElasticsearchDTO.setSortValues(objects);

            findDynamicElasticsearchList.add(findDynamicElasticsearchDTO);
        }
        return findDynamicElasticsearchList;
    }

    /**
     * 关注列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @param userIdList
     * @param isAudit
     * @param channel
     * @return
     */
    public List<FindDynamicVO> selectAttentionPage(long userId, Integer page, Integer pageSize,
                                                   Set<Long> userIdList, boolean isAudit, String channel) throws IOException {

        List<FindDynamicVO> resultList = new ArrayList<>();

        int startNum = PageUtil.getOffset(page, pageSize);

        SearchRequest searchRequest = getSearchRequest("find_dynamic_info");

        List<SortBuilder> sortBuilderList = new ArrayList<>(1);

        sortBuilderList.add(new FieldSortBuilder("approvalCreateTime").order(SortOrder.DESC));

        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(pageSize, startNum, sortBuilderList);

        if (isAudit) {
            Appkey appkey = appKeyService.getAppKeyByKey(AUDIT_USER_IDS_KEY);
            if (appkey != null) {
                String target = appkey.getValue();
                if (StringUtils.isNotBlank(target) && target.split(",").length > 0) {
                    String[] userIds = target.split(",");
                    Set<Long> userIdSet = new HashSet<>();
                    for (String value : userIds) {
                        userIdSet.add(Long.parseLong(value));
                    }
                    userIdList.retainAll(userIdSet);
                } else {
                    return resultList;
                }
            } else {
                return resultList;
            }
        } else {

        }

        List<TermsQueryBuilder> termsQueryBuilderList=getTermsQueryBuildersByShieldType(channel,isAudit);

        termsQueryBuilderList.add(QueryBuilders.termsQuery("userId", userIdList));

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        for(TermsQueryBuilder termsQueryBuilder: termsQueryBuilderList){
            boolQuery.must(termsQueryBuilder);
        }
        searchSourceBuilder.query(boolQuery);

        searchRequest.source(searchSourceBuilder);

        SearchResponse result = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<FindDynamicElasticsearchDTO> findDynamicElasticsearchList = getFindDynamicElasticsearchDTOS(result);

        for (FindDynamicElasticsearchDTO obj : findDynamicElasticsearchList) {
            FindDynamicVO findDynamicVO = getFindDynamicVO(userId, obj);
            resultList.add(findDynamicVO);
        }

        return resultList;
    }


    private SearchSourceBuilder getSearchSourceBuilder(Integer pageSize, int startNum,
                                                       List<SortBuilder> sortBuilderList) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));

        searchSourceBuilder.from(startNum);

        searchSourceBuilder.size(pageSize);

        if (sortBuilderList != null && sortBuilderList.size() > 0) {
            for (SortBuilder sortBuilder : sortBuilderList) {
                searchSourceBuilder.sort(sortBuilder);
            }
        }

        return searchSourceBuilder;
    }


    private SearchRequest getSearchRequest(String index) {
        return new SearchRequest(index);
    }

    public List<FindDynamicVO> selectVicinityPage(long userId, String location,
                                                  Integer page, Integer pageSize, boolean isAudit, String channel) throws IOException {

        int startNum = PageUtil.getOffset(page, pageSize);

        SearchRequest searchRequest = getSearchRequest("find_dynamic_info");

        List<SortBuilder> sortBuilderList = new ArrayList<>();

        if (StringUtils.isNotBlank(location)) {
            GeoPoint geoPoint = new GeoPoint(location);
            GeoDistanceSortBuilder geoDistanceSortBuilder = new GeoDistanceSortBuilder("location", geoPoint);
            geoDistanceSortBuilder.order(SortOrder.ASC);
            geoDistanceSortBuilder.geoDistance(GeoDistance.PLANE);
            geoDistanceSortBuilder.unit(DistanceUnit.KILOMETERS);
            geoDistanceSortBuilder.sortMode(SortMode.MAX);
            sortBuilderList.add(geoDistanceSortBuilder);

            FieldSortBuilder fieldSortBuilder = new FieldSortBuilder("approvalCreateDate").order(SortOrder.DESC);

            sortBuilderList.add(fieldSortBuilder);
        } else {
            FieldSortBuilder fieldSortBuilder = new FieldSortBuilder("approvalCreateTime").order(SortOrder.DESC);

            sortBuilderList.add(fieldSortBuilder);
        }

        List<FindDynamicVO> resultList = new ArrayList<>();

        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(pageSize, startNum, sortBuilderList);
        List<TermsQueryBuilder> termsQueryBuilderList = new ArrayList<>();
        if (isAudit) {
            Appkey appkey = appKeyService.getAppKeyByKey(AUDIT_USER_IDS_KEY);
            if (appkey != null) {
                String target = appkey.getValue();
                if (StringUtils.isNotBlank(target) && target.split(",").length > 0) {
                    String[] userIds = target.split(",");
                    termsQueryBuilderList.add(QueryBuilders.termsQuery("userId", userIds));
                } else {
                    return resultList;
                }
            } else {
                return resultList;
            }
        }

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(termsQueryBuilderList.size()>0){
            for(TermsQueryBuilder termsQueryBuilder:termsQueryBuilderList){
                boolQueryBuilder.must(termsQueryBuilder);
            }
            searchSourceBuilder.query(boolQueryBuilder);
        }else{
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        }

        searchRequest.source(searchSourceBuilder);


        SearchResponse result = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<FindDynamicElasticsearchDTO> findDynamicElasticsearchList = getFindDynamicElasticsearchDTOS(result);


        for (FindDynamicElasticsearchDTO obj : findDynamicElasticsearchList) {

            FindDynamicVO findDynamicVO = getFindDynamicVO(userId, obj);
            if (StringUtils.isBlank(location)) {
                findDynamicVO.setDistance("未知");
            } else {
                double km = 100000000.00;
                for (Object value : obj.getSortValues()) {
                    if (value instanceof Double) {
                        km = ((Double) value).doubleValue();
                        break;
                    }
                }

                if (km < 1) {
                    findDynamicVO.setDistance("<1km");
                } else if (km > 800) {
                    findDynamicVO.setDistance(">800km");
                } else {
                    BigDecimal bg = new BigDecimal(km);
                    double distance = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                    findDynamicVO.setDistance(distance + "km");
                }

            }
            resultList.add(findDynamicVO);

        }

        return resultList;

    }

    /**
     * 更新，点赞数减一
     *
     * @param findId
     */
    public void subtractionLikeNumber(int findId) throws IOException {
        UpdateRequest request = new UpdateRequest("find_dynamic_info", "findDynamicInfo", findId + "");
        Script inline = new Script("ctx._source.likeNumber -=1");
        request.script(inline);
        request.scriptedUpsert(true);
        restHighLevelClient.update(request, RequestOptions.DEFAULT);
    }

    public void addFindDynamicLikeNumber(int findId) throws IOException {
        UpdateRequest request = new UpdateRequest("find_dynamic_info", "findDynamicInfo", findId + "");
        Script inline = new Script("ctx._source.likeNumber +=1");
        request.script(inline);
        request.scriptedUpsert(true);
        restHighLevelClient.update(request, RequestOptions.DEFAULT);

    }

    /**
     * 个人主页
     *
     * @param userId
     * @param toUserId 目标用户id
     * @param page
     * @param pageSize
     * @param channel
     * @return
     */
    public List<FindDynamicVO> selectIndividualPage(long userId, Long toUserId, Integer page, Integer pageSize,
                                                    String channel) throws IOException {

        List<SortBuilder> sortBuilderList = new ArrayList<>(1);

        int startNum = PageUtil.getOffset(page, pageSize);

        SearchRequest searchRequest = getSearchRequest("find_dynamic_info");

        FieldSortBuilder fieldSortBuilder = new FieldSortBuilder("approvalCreateTime").order(SortOrder.DESC);

        sortBuilderList.add(fieldSortBuilder);

        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(pageSize, startNum, sortBuilderList);

        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        List<TermsQueryBuilder> termsQueryBuilderList = getTermsQueryBuildersByShieldType(channel,false);

        for(TermsQueryBuilder termsQueryBuilder : termsQueryBuilderList){
            boolQueryBuilder.must(termsQueryBuilder);
        }

        boolQueryBuilder.must(QueryBuilders.termQuery("userId", toUserId));

        searchSourceBuilder.query(boolQueryBuilder);

        searchRequest.source(searchSourceBuilder);


        List<FindDynamicVO> resultList = new ArrayList<>();

        SearchResponse result = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<FindDynamicElasticsearchDTO> findDynamicElasticsearchList = getFindDynamicElasticsearchDTOS(result);

        for (FindDynamicElasticsearchDTO obj : findDynamicElasticsearchList) {
            FindDynamicVO findDynamicVO = getFindDynamicVO(userId, obj);
            findDynamicVO.setDistance("未知");
            resultList.add(findDynamicVO);
        }

        return resultList;
    }


    /**
     * 根据渠道号获取屏蔽
     * @param channel
     * @param isAudit 审核状态，true 为审核状态，false为关闭审核状态
     * @return
     */
    private List<TermsQueryBuilder> getTermsQueryBuildersByShieldType(String channel,boolean isAudit) {
        List<TermsQueryBuilder> termsQueryBuilderList = new ArrayList<>();
        if(!isAudit){
            boolean bl = findDynamicInfoChannelService.getShieldType(channel);
            if (bl) {
                //pronGrade 1：小尺度，2：为大尺度
                TermsQueryBuilder pronGradeQuery = QueryBuilders.termsQuery("pronGrade", "1");
                termsQueryBuilderList.add(pronGradeQuery);
            }
        }

        return termsQueryBuilderList;
    }
}
